001 /* 002 * Copyright 2004-2005 Stephen McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.transit.tools; 020 021 import java.io.IOException; 022 import java.io.InputStream; 023 024 import java.net.URI; 025 import java.util.Map; 026 import java.util.Hashtable; 027 028 import java.util.List; 029 import java.util.ArrayList; 030 031 import net.dpml.util.ElementHelper; 032 033 import net.dpml.lang.Part; 034 import net.dpml.lang.Resource; 035 036 import org.apache.tools.ant.BuildException; 037 import org.apache.tools.ant.BuildListener; 038 import org.apache.tools.ant.Project; 039 import org.apache.tools.ant.ComponentHelper; 040 041 import org.w3c.dom.Element; 042 043 /** 044 * The plugin task handles the establishment of ant tasks, listeners, and antlibs derived 045 * from a classloader established by the transit sub-system. 046 * 047 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 048 * @version 1.0.1 049 */ 050 public class PluginTask extends TransitTask 051 { 052 private Map m_map = new Hashtable(); 053 054 /** 055 * The uri of the plugin to load. 056 */ 057 private String m_uri; 058 059 /** 060 * Overloaded plugin urn. 061 */ 062 private String m_urn; 063 064 /** 065 * A list of tasks declared by the plugin declaration. 066 */ 067 private List m_tasks = new ArrayList(); 068 069 /** 070 * List of listeners declared by the plugin task declaration. 071 */ 072 private List m_listeners = new ArrayList(); 073 074 /** 075 * List of antlibs declared by the plugin task declaration. 076 */ 077 private List m_antlibs = new ArrayList(); 078 079 /** 080 * Plugin name delalred by the plugin task declaration. 081 */ 082 private String m_name; 083 084 /** 085 * A flag indicating that nested directives have been provided. 086 */ 087 private boolean m_flag = false; 088 089 /** 090 * Set the project. 091 * @param project the current project 092 */ 093 public void setProject( Project project ) 094 { 095 setTaskName( "plugin" ); 096 super.setProject( project ); 097 } 098 099 /** 100 * Create and associate a new antlib urn entry with the plugin. 101 * @return the new antlib entry 102 */ 103 public Antlib createAntlib() 104 { 105 m_flag = true; 106 final Antlib antlib = new Antlib(); 107 m_antlibs.add( antlib ); 108 return antlib; 109 } 110 111 /** 112 * Create and associate a new task entry with the plugin. 113 * @return the new task entry 114 */ 115 public Task createTask() 116 { 117 m_flag = true; 118 final Task task = new Task(); 119 m_tasks.add( task ); 120 return task; 121 } 122 123 /** 124 * Create and associate a new build listener with the plugin. 125 * @return the new listener entry 126 */ 127 public Listener createListener() 128 { 129 m_flag = true; 130 final Listener listener = new Listener(); 131 m_listeners.add( listener ); 132 return listener; 133 } 134 135 /** 136 * Set the artifact uri of the plugin from which the task is to be loaded. 137 * @param uri an artifact plugin uri 138 */ 139 public void setUri( String uri ) 140 { 141 m_uri = uri; 142 } 143 144 /** 145 * Overload the urn to assign to the plugin. 146 * @param urn the urn to use 147 */ 148 public void setUrn( String urn ) 149 { 150 m_urn = urn; 151 } 152 153 /** 154 * Return the artifact uri of the plugin. 155 * @return the plugin uri 156 */ 157 public URI getUri() 158 { 159 try 160 { 161 return new URI( m_uri ); 162 } 163 catch( Throwable e ) 164 { 165 final String error = 166 "Cound not convert the supplied uri spec [" 167 + m_uri 168 + "] to a formal URI."; 169 throw new BuildException( error, e, getLocation() ); 170 } 171 } 172 173 /** 174 * Load the plugin and handle registration of listeners, tasks, and antlib 175 * declarations based on the nested nested task directives. 176 * @exception BuildException if an error occurs during plugin loading or deployment 177 */ 178 public void execute() throws BuildException 179 { 180 if( null == m_uri ) 181 { 182 final String error = 183 "Missing uri attribute."; 184 throw new BuildException( error ); 185 } 186 187 final Project project = getProject(); 188 final ComponentHelper helper = 189 ComponentHelper.getComponentHelper( project ); 190 191 if( !m_flag ) 192 { 193 createAntlib(); 194 } 195 196 try 197 { 198 URI uri = new URI( m_uri ); 199 Part part = getPart( uri ); 200 201 ClassLoader loader = part.getClassLoader(); 202 Task[] tasks = (Task[]) m_tasks.toArray( new Task[0] ); 203 if( tasks.length > 0 ) 204 { 205 for( int i=0; i < tasks.length; i++ ) 206 { 207 try 208 { 209 Task task = tasks[i]; 210 String classname = task.getClassname(); 211 String name = task.getName(); 212 Class c = loader.loadClass( classname ); 213 helper.addTaskDefinition( name, c ); 214 } 215 catch( Throwable e ) 216 { 217 final String error = 218 "Failed to load a named task [" 219 + tasks[i].getName() 220 + "] from the plugin [" 221 + uri 222 + "]."; 223 throw new BuildException( error, e, getLocation() ); 224 } 225 } 226 } 227 228 Listener[] listeners = (Listener[]) m_listeners.toArray( new Listener[0] ); 229 for( int i=0; i < listeners.length; i++ ) 230 { 231 Listener listener = listeners[i]; 232 String classname = listener.getClassname(); 233 Class c = loader.loadClass( classname ); 234 Object object = c.newInstance(); 235 if( object instanceof BuildListener ) 236 { 237 BuildListener instance = (BuildListener) object; 238 getProject().addBuildListener( instance ); 239 log( "registered listener: " + instance.getClass().getName() ); 240 } 241 else 242 { 243 final String error = 244 "The plugin [" 245 + uri 246 + "] establishing the class [" 247 + object.getClass().getName() 248 + "] could not be registered as a project listener because it does not implement the [" 249 + BuildListener.class.getName() 250 + "] interface."; 251 throw new BuildException( error, getLocation() ); 252 } 253 } 254 255 Antlib[] antlibs = (Antlib[]) m_antlibs.toArray( new Antlib[0] ); 256 for( int i=0; i < antlibs.length; i++ ) 257 { 258 loadAntlib( uri, loader, helper, antlibs[i] ); 259 } 260 } 261 catch( BuildException e ) 262 { 263 throw e; 264 } 265 catch( Throwable e ) 266 { 267 final String error = "Unable to load the plugin [" 268 + m_uri 269 + "] due to " 270 + e.toString(); 271 throw new BuildException( error, e, getLocation() ); 272 } 273 } 274 275 private Part getPart( URI uri ) throws IOException 276 { 277 return Part.load( uri, true ); 278 } 279 280 /** 281 * Load an antlib. 282 * @param classloader the classloader from which the antlib will be loaded 283 * @param helper the component helper 284 * @param antlib the antlib to load 285 * @exception Exception if it doesn't work out 286 */ 287 private void loadAntlib( 288 URI uri, ClassLoader classloader, ComponentHelper helper, Antlib antlib ) throws Exception 289 { 290 Part part = getPart( uri ); 291 292 String resource = antlib.getPath(); 293 if( null == resource ) 294 { 295 if( part instanceof Resource ) 296 { 297 Resource res = (Resource) part; 298 resource = res.getPath(); 299 } 300 } 301 if( null == resource ) 302 { 303 final String error = 304 "Resource path for the antlib is not declared in the plugin descriptor " 305 + "or antlib directive [" 306 + uri 307 + "]"; 308 throw new BuildException( error, getLocation() ); 309 } 310 311 String urn = getAntLibURN( antlib, part ); 312 if( null == urn ) 313 { 314 final String error = 315 "URN for the antlib is not declared in the plugin descriptor " 316 + "or antlib directive [" 317 + uri 318 + "]"; 319 throw new BuildException( error, getLocation() ); 320 } 321 322 InputStream input = classloader.getResourceAsStream( resource ); 323 Element root = ElementHelper.getRootElement( input ); 324 Element[] tasks = ElementHelper.getChildren( root, "taskdef" ); 325 for( int i=0; i < tasks.length; i++ ) 326 { 327 Element task = tasks[i]; 328 String name = ElementHelper.getAttribute( task, "name" ); 329 String classname = ElementHelper.getAttribute( task, "classname" ); 330 loadTaskDef( classloader, helper, classname, urn + ":" + name ); 331 } 332 333 Element[] types = ElementHelper.getChildren( root, "typedef" ); 334 for( int i=0; i < types.length; i++ ) 335 { 336 Element type = types[i]; 337 String name = ElementHelper.getAttribute( type, "name" ); 338 String classname = ElementHelper.getAttribute( type, "classname" ); 339 loadTypeDef( classloader, helper, classname, urn + ":" + name ); 340 } 341 } 342 343 private String getAntLibURN( Antlib antlib, Part part ) 344 { 345 if( null != m_urn ) 346 { 347 return m_urn; 348 } 349 String urn = antlib.getURN(); 350 if( null != urn ) 351 { 352 return urn; 353 } 354 else 355 { 356 if( part instanceof Resource ) 357 { 358 Resource res = (Resource) part; 359 return res.getURN(); 360 } 361 else 362 { 363 return null; 364 } 365 } 366 } 367 368 /** 369 * Load a single task defintion. 370 * @param loader the classloader from which the task will be loaded 371 * @param helper the component helper 372 * @param classname the task classname 373 * @param name the task name 374 * @exception BuildException if an error occurs while attempting to load the task 375 */ 376 private void loadTaskDef( ClassLoader loader, ComponentHelper helper, String classname, String name ) 377 throws BuildException 378 { 379 if( getProject().getTaskDefinitions().get( name ) != null ) 380 { 381 return; 382 } 383 384 try 385 { 386 Class c = loader.loadClass( classname ); 387 helper.addTaskDefinition( name, c ); 388 log( "installed taskdef: " + name, Project.MSG_VERBOSE ); 389 } 390 catch( BuildException e ) 391 { 392 throw e; 393 } 394 catch( Throwable e ) 395 { 396 final String error = 397 "Unable to load task [" 398 + name 399 + "] from class [" 400 + classname 401 + "]."; 402 throw new BuildException( error, e, getLocation() ); 403 } 404 } 405 406 /** 407 * Load a single type defintion. 408 * @param loader the classloader from which the type will be loaded 409 * @param helper the component helper 410 * @param classname the type classname 411 * @param name the task type 412 * @exception BuildException if an error occurs while attempting to load the task 413 */ 414 private void loadTypeDef( ClassLoader loader, ComponentHelper helper, String classname, String name ) 415 throws BuildException 416 { 417 if( getProject().getDataTypeDefinitions().get( name ) != null ) 418 { 419 return; 420 } 421 422 try 423 { 424 Class c = loader.loadClass( classname ); 425 helper.addDataTypeDefinition( name, c ); 426 log( "installed typedef: " + name, Project.MSG_VERBOSE ); 427 } 428 catch( BuildException e ) 429 { 430 throw e; 431 } 432 catch( Throwable e ) 433 { 434 final String error = 435 "Unable to load type [" 436 + name + "] from class [" 437 + classname 438 + "]."; 439 throw new BuildException( error, e, getLocation() ); 440 } 441 } 442 443 /** 444 * Nested element with the <plugin> element declaring the name and class of 445 * a task to be loaded from the classloader established by the transit plugin descriptor. 446 */ 447 public static class Task 448 { 449 /** 450 * The task name. 451 */ 452 private String m_name; 453 454 /** 455 * The task classname. 456 */ 457 private String m_classname; 458 459 /** 460 * Set the task name. 461 * @param name the name of the task 462 */ 463 public void setName( final String name ) 464 { 465 m_name = name; 466 } 467 468 /** 469 * Set the task classname. 470 * @param classname the task classname 471 */ 472 public void setClass( final String classname ) 473 { 474 m_classname = classname; 475 } 476 477 /** 478 * Return the task classname. 479 * @return the classname 480 * @exception BuildException if the class attribute is missing 481 */ 482 public String getClassname() throws BuildException 483 { 484 if( null == m_classname ) 485 { 486 final String error = 487 "Missing class attribute."; 488 throw new BuildException( error ); 489 } 490 return m_classname; 491 } 492 493 /** 494 * Return the task name. 495 * @return the name 496 * @exception BuildException if the name attribute is missing 497 */ 498 public String getName() throws BuildException 499 { 500 if( null == m_name ) 501 { 502 final String error = 503 "Missing name attribute."; 504 throw new BuildException( error ); 505 } 506 return m_name; 507 } 508 } 509 510 /** 511 * Nested element with the <plugin> element declaring the name and class of 512 * a project listener to be loaded from the classloader established by the transit plugin descriptor. 513 */ 514 public static class Listener 515 { 516 /** 517 * The listener classname. 518 */ 519 private String m_classname; 520 521 /** 522 * Set the task classname. 523 * @param classname the task classname 524 */ 525 public void setClass( final String classname ) 526 { 527 m_classname = classname; 528 } 529 530 /** 531 * Return the task classname. 532 * @return the classname 533 * @exception BuildException if the class attribute is missing 534 */ 535 public String getClassname() throws BuildException 536 { 537 if( null == m_classname ) 538 { 539 final String error = 540 "Missing class attribute."; 541 throw new BuildException( error ); 542 } 543 return m_classname; 544 } 545 } 546 547 /** 548 * Nested element with the <plugin> element declaring a packaged resource and urn of 549 * an antlib descriptor to be loaded from the classloader established by the transit plugin 550 * descriptor. 551 */ 552 public static class Antlib 553 { 554 /** 555 * The antlib urn. 556 */ 557 private String m_urn; 558 559 /** 560 * The antlib descriptor resource path. 561 */ 562 private String m_path; 563 564 /** 565 * Set the urn for this antlib. 566 * @param urn the antlib urn 567 */ 568 public void setUrn( final String urn ) 569 { 570 m_urn = urn; 571 } 572 573 /** 574 * Return the antlib urn. 575 * @return the urn (possibly null in which case the a urn must be declared 576 * within the plugin descriptor)0 577 */ 578 public String getURN() 579 { 580 return m_urn; 581 } 582 583 /** 584 * Set the antlib resource path 585 * @param path the resource path 586 */ 587 public void setResource( final String path ) 588 { 589 m_path = path; 590 } 591 592 /** 593 * Return the antlib resource path. 594 * @return the path (possibly null in which case the resource reference 595 * must exist in the plugin descriptor) 596 */ 597 public String getPath() 598 { 599 return m_path; 600 } 601 } 602 }